home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / wnos / wn941101 / ax25.asm < prev    next >
Encoding:
Assembly Source File  |  1994-09-24  |  66.8 KB  |  2,368 lines

  1. ;AX.25 packet driver for RS232 port by Pawel Jalocha
  2. ;version of 15th January 1993
  3.  
  4. ;The purpose of this driver is to provide interface
  5. ;between simple modem (a la BAYCOM) and higher level application
  6. ;software like KA9Q NOS.
  7.  
  8. ;ax25.com emulates HDLC chip, buffers frames
  9. ;and serves software interface to higher applications
  10. ;by mean of a selected software interrupt.
  11.  
  12. ;Driver's calling convention conforms to "FTP packet driver specification"
  13. ;but no other tests than with NOS have been done.
  14.  
  15. ;Currently the driver provides only _one_ handle at a time.
  16.  
  17. ;To make ax25.com from ax25.asm execute two commands:
  18. ;    tasm ax25
  19. ;    tlink /t ax25
  20.  
  21. ;Free license for this software is herein granted for all radio _amateurs_
  22. ;Commercial usage in whole or part is prohibited.
  23.  
  24. ;Questions may be addressed to:
  25. ;       email:  jalocha@chopin.ifj.edu.pl
  26. ;       or      jalocha@vxcern.cern.ch
  27. ;       packet: SR9VRC@SP9ZDN.POL.EU (don't know whether it really works)
  28.  
  29. ;  To change ... => look for definition of ...
  30. ;
  31. ;o Maximum frame size the driver can receive => MaxFrameLen
  32. ;
  33. ;o Maximum amount of data the driver can transmit per one PTT push
  34. ;  and thus maximum transmition frame length => TxBufferLen
  35. ;
  36. ;o Size of an auxiliary buffer storing periods between signal
  37. ;  transitions before they are analyzed => RxBufferLen
  38. ;
  39. ;  If you don't need large frames you may save some kilobytes
  40. ;  of resident RAM by making MaxFrameLen and TxBufferLen smaller.
  41. ;
  42. ;  For low speeds you may as well carefully decrease RxBufferLen
  43.  
  44. ;You may want to change drvr_class to SLIP and run point-to-point direct IP
  45.  
  46. ;==============================================================
  47.  
  48. ; dl6zba 940825
  49. ; quick hack to allow control of fullduplex mode from outside, while the
  50. ; driver is already installed, that is.
  51. ; function number = 128, cx = 1 means fullduplex, cx=0 means csma
  52. ; 940827        save old carrier_sense variable before changing to fullduplex
  53.  
  54. .8086
  55.  
  56. ax25_code       segment word public
  57.         assume  cs:ax25_code, ds:ax25_code
  58.  
  59.     org 2ch
  60. phd_environ     dw ?
  61.  
  62.     org 100h                ;when .com file is executed DOS jumps here
  63. start:  jmp install_driver
  64.  
  65.         even
  66. ;primary parameters with default values
  67. packet_int_no   db 60h  ;software interrupt
  68. com_irq         db 4    ;COM port IRQ
  69. com_irq_prio    db 0    ;if non-zero the IRQ should have highest priority.
  70. com_irq_share   db 0    ;if non-zero the IRQ is shared with other driver(s).
  71. com_base        dw 3f8h ;COM port base
  72. bit_rate        dw 1200 ;speed in bits per second
  73. tx_head         dw 240  ;transmition header length in bits
  74. tx_tail         dw 24   ;transmition tail length in bits
  75. slot_time       db 120  ;slot time for carrier sensing in bits
  76. persistance     db 64   ;p-persistence
  77. carrier_sense   db 2    ;carrier sensing
  78.             ;0 - don't care - just transmit (full duplex)
  79.             ;1 - sense DCD line
  80.             ;2 - sense data transitions (BAYCOM-like)
  81.             ;3 - deliver from data analysis (software DCD)
  82. drvr_class      db 9    ;driver's class - default is 9 that is AX.25
  83. sound           db 0    ;sound effects enabled if non-zero
  84. walk_step_div   db 16   ;bit_time/walk_step ratio
  85. dcd_thres       dw 50   ;DCD squelch threshold (0..100)
  86.  
  87. ;secondary parameters computed from primary ones
  88. cl_bit_len      dw 0    ;bit len in system clock (8253/4) ticks
  89. cl_bit_len_2    dw 0    ;half bit len in clock ticks
  90. cl_dcd_thres    dw 0    ;DCD threshold in clock ticks
  91. walk_step       dw 0    ;random walk step for DPLL
  92. bd_slot_time    dw 0    ;slot time in baud generator ticks / 8
  93. bd_bit_len      db 0    ;bit len in baud generator ticks / 8
  94. irq_mask        db 0    ;IRQ mask for 8259
  95.  
  96. driver_name     db 'AX.25 driver for BAYCOM-style modem',0
  97.  
  98.         even
  99. old_packet_int  dw 0,0  ;saves software interrupt vector
  100. receive_upcall  dw 0,0  ;keeps application "upcall" routine address
  101.  
  102. ;ANSI sequences to set char. rendition.
  103. ;if you don't like these or have problems with ANSI
  104. ;make null strings definitions like: BLINK equ ''
  105.  
  106. ;NORM    equ     27,'[0m'
  107. NORM    equ     ' '
  108. ;BLINK   equ     27,'[5m'
  109. BLINK   equ     ' '
  110. ;REVERS  equ     27,'[7m'
  111. REVERS  equ     ' '
  112. ;BOLD    equ     27,'[1m'
  113. BOLD    equ     ' '
  114.  
  115. ;old carrier_sense value        dl6zba 940827
  116. old_cs  db      2
  117.  
  118. ;==============================================================
  119. ;Service routine for software interrupt to control the driver
  120.  
  121. ;  Packet Driver Error numbers
  122. NO_ERROR        equ     0       ;no error at all.
  123. BAD_HANDLE      equ     1       ;invalid handle number
  124. NO_CLASS        equ     2       ;no interfaces of specified class found
  125. NO_TYPE         equ     3       ;no interfaces of specified type found
  126. NO_NUMBER       equ     4       ;no interfaces of specified number found
  127. BAD_TYPE        equ     5       ;bad packet type specified
  128. NO_MULTICAST    equ     6       ;this interface does not support multicast
  129. CANT_TERMINATE  equ     7       ;this packet driver cannot terminate
  130. BAD_MODE        equ     8       ;an invalid receiver mode was specified
  131. NO_SPACE        equ     9       ;operation failed because of insufficient space
  132. TYPE_INUSE      equ     10      ;the type had previously been accessed, and not released.
  133. BAD_COMMAND     equ     11      ;the command was out of range, or not implemented
  134. CANT_SEND       equ     12      ;the packet couldn't be sent (usually hardware error)
  135. CANT_SET        equ     13      ;hardware address couldn't be changed (more than 1 handle open)
  136. BAD_ADDRESS     equ     14      ;hardware address has bad length or format
  137. CANT_RESET      equ     15      ;Couldn't reset interface (more than 1 handle open).
  138. BAD_IOCB        equ     16      ;an invalid iocb was specified
  139.  
  140. regs_w  struc                           ; stack offsets of incoming regs
  141. _ES     dw      ?
  142. _DS     dw      ?
  143. _BP     dw      ?
  144. _DI     dw      ?
  145. _SI     dw      ?
  146. _DX     dw      ?
  147. _CX     dw      ?
  148. _BX     dw      ?
  149. _AX     dw      ?
  150. _IP     dw      ?
  151. _CS     dw      ?
  152. _F      dw      ?                       ; flags, Carry flag is bit 0
  153. regs_w  ends
  154.  
  155. CY      equ     0001h
  156. EI      equ     0200h
  157.  
  158. regs_b  struc                           ; stack offsets of incoming regs
  159.     dw      ?                       ; es, ds, bp, di, si are 16 bits
  160.     dw      ?
  161.     dw      ?
  162.     dw      ?
  163.     dw      ?
  164. _DL     db      ?
  165. _DH     db      ?
  166. _CL     db      ?
  167. _CH     db      ?
  168. _BL     db      ?
  169. _BH     db      ?
  170. _AL     db      ?
  171. _AH     db      ?
  172. regs_b  ends
  173.  
  174. DRVR_ISR:                       ;service interrupt vector points here
  175.                 ;application layer calls enter this point
  176.                 ;via a software interrupt
  177.     jmp exec_command        ;entry point must be a jump
  178.     db 'PKT DRVR',0         ;followed by this string
  179.  
  180. exec_command:           ;packet driver command executor
  181.     sti             ;don't lock interrupts
  182.     push ax         ;save registers on stack
  183.     push bx
  184.     push cx
  185.     push dx
  186.     push si
  187.     push di
  188.     push bp
  189.     push ds
  190.     push es
  191.     mov  bp,sp              ;bp=sp so we can address pushed registers
  192.     and _F[bp],not CY       ;Clear carry on exit
  193.     mov bx,cs               ;make ds=cs
  194.     mov ds,bx
  195.     mov bl,ah               ;execute command given by ah
  196.     mov bh,0
  197.     cmp bx,26
  198.         jc  cmd_ok              ; *** zba
  199.         cmp bx,128              ; *   handle function number #128
  200.         jne f_above_25          ; *   (fullduplex control extension)
  201.         mov bx,26               ; *   quick workaround...
  202. cmd_ok: add bx,bx
  203.     call [functions+bx]
  204. DRVR_ISR_return:
  205.     mov _DH[bp],dh          ;pass dh-now to dh-on-exit
  206.     sbb ax,ax
  207.     and ax,CY
  208.     or _F[bp],ax            ;pass carry-now to carry-on-exit
  209.     pop es
  210.     pop ds
  211.     pop bp
  212.     pop di
  213.     pop si
  214.     pop dx
  215.     pop cx
  216.     pop bx
  217.     pop ax
  218.     iret
  219.  
  220. f_above_25:
  221.     call f_not_implemented
  222.     jmp short DRVR_ISR_return
  223.  
  224.     even
  225. functions       label   word
  226.     dw      f_not_implemented       ;0
  227.     dw      f_driver_info           ;1
  228.     dw      f_access_type           ;2
  229.     dw      f_release_type          ;3
  230.     dw      f_send_pkt              ;4
  231.     dw      f_terminate             ;5
  232.     dw      f_get_address           ;6
  233.     dw      f_reset_interface       ;7
  234.     dw      f_stop                  ;8
  235.     dw      f_not_implemented       ;9
  236.     dw      f_get_parameters        ;10
  237.     dw      f_not_implemented       ;11
  238.     dw      f_as_send_pkt           ;12
  239.     dw      f_drop_pkt              ;13
  240.     dw      f_not_implemented       ;14
  241.     dw      f_not_implemented       ;15
  242.     dw      f_not_implemented       ;16
  243.     dw      f_not_implemented       ;17
  244.     dw      f_not_implemented       ;18
  245.     dw      f_not_implemented       ;19
  246.     dw      f_set_rcv_mode          ;20
  247.     dw      f_get_rcv_mode          ;21
  248.     dw      f_set_multicast_list    ;22
  249.     dw      f_get_multicast_list    ;23
  250.     dw      f_get_statistics        ;24
  251.     dw      f_set_address           ;25
  252.         dw      f_fullduplex            ;26 [128 actually]
  253.  
  254. f_driver_info:
  255.     mov dh,drvr_class               ;driver class
  256.     mov _CH[bp],dh
  257.     mov _AL[bp],1                   ;basic flag
  258.     mov _DX[bp],0                   ;driver type
  259.     mov _CL[bp],0                   ;driver number
  260.     mov _BX[bp],0                   ;driver version
  261.     mov _DS[bp],ds                  ;driver name pointer
  262.     mov _SI[bp],offset driver_name
  263.     mov dh,NO_ERROR
  264.     clc
  265.     ret
  266.  
  267. f_access_type:
  268.     mov bx,_BX[bp]
  269.     cmp al,drvr_class       ;our class ?
  270.     jnz wrong_class
  271.     cmp bx,0FFFFh           ;generic type ?
  272.     jz type_OK
  273.     cmp bx,0                ;our type ?
  274.     jnz wrong_type
  275. type_OK:
  276.     cmp dl,0                ;generic num ?
  277.     jnz wrong_num
  278.     mov ax,receive_upcall   ;check if handle busy
  279.     or ax,receive_upcall+2
  280.     jnz busy_handle
  281.     mov receive_upcall,di   ;store receiver upcall
  282.     mov ax,es
  283.     mov receive_upcall+2,ax
  284.     mov _AX[bp],0           ;return handle=0
  285.     clc
  286.     mov dh,NO_ERROR
  287.     ret
  288.  
  289. wrong_class:
  290.     stc
  291.     mov dh,NO_CLASS
  292.     ret
  293. wrong_type:
  294.     stc
  295.     mov dh,NO_TYPE
  296.     ret
  297. wrong_num:
  298.     stc
  299.     mov dh,NO_NUMBER
  300.     ret
  301. busy_handle:
  302.     stc
  303.     mov dh,TYPE_INUSE
  304.     ret
  305.  
  306. f_stop: jmp short clear_upcall
  307.  
  308. f_release_type:
  309.     cmp _BX[bp],0           ;handle=0 ?
  310.     jnz wrong_handle
  311.     mov ax,receive_upcall   ;is receiver upcall defined ?
  312.     or ax,receive_upcall+2
  313.     jz wrong_handle         ;jump if not
  314. clear_upcall:
  315.     xor ax,ax               ;receiver upcall := 0:0
  316.     mov receive_upcall,ax   ;this means "upcall address is not valid"
  317.     mov receive_upcall+2,ax
  318.     clc
  319.     mov dh,NO_ERROR
  320.     ret
  321.  
  322. wrong_handle:
  323.     stc
  324.     mov dh,BAD_HANDLE
  325.     ret
  326.  
  327. f_send_pkt:                     ;_DS:si=data, cx=length
  328.     mov es,_DS[bp]          ;es:si=packet address, cx=packet length
  329.  
  330.     mov bx,cx               ;save packet length
  331.     mov ah,7Eh              ;starting HDLC flag
  332.     call AddTxByteDirect    ;add to TxBuffer without bit stuffing
  333.     jc PacketTooBig         ;jump if TxBuffer full
  334.     mov dx,0FFFFh           ;initialize CRC
  335. AddNextByte:
  336.       mov ah,es:[si]        ;take next packet byte
  337.       inc si                ;increment pointer
  338.       call CRCpass          ;pass through CRC
  339.       call AddTxByteStuffing ;add the byte to TxBuffer _with_ stuffing
  340.       jc PacketTooBig       ;jump if TxBuffer overloaded
  341.     loop AddNextByte        ;loop over packet's bytes
  342.     not dx                  ;complete CRC computation by inverting all bits
  343.     mov ax,dx               ;append CRC
  344.     xchg al,ah              ;lower byte first
  345.     call AddTxByteStuffing  ;add to TxBuffer with stuffing
  346.     jc PacketTooBig
  347.     xchg al,ah              ;higher byte now
  348.     call AddTxByteStuffing  ;add to TxBuffer
  349.     jc PacketTooBig
  350.     mov ah,7Eh              ;ending HDLC flag
  351.     call AddTxByteDirect    ;add to TxBuffer _without_ bit stuffing
  352.     jc PacketTooBig
  353.     call TxFlush8bit        ;ensure that TxBuffer data ends at byte boundary
  354.     jz PacketTooBig
  355.  
  356.     call ValidateTxBlock    ;make the packet we just put into buffer
  357.                 ;valid for transmition
  358.  
  359.     mov ax,bx               ;increment bytes_out counter
  360.     mov bx,offset bytes_out ;note that we still had the packet length in bx
  361.     call inc_dword_bx_by_ax ;dword [bx]+=ax
  362.  
  363.     mov bx,offset packets_out       ;increment packet_out counter
  364.     call inc_dword_bx       ;dword [bx]+=1
  365.  
  366. ;Note that packets_out/bytes_out counts data sent by application
  367. ;that is _not_ the data transmitted from the buffer.
  368. ;Data sent by application will be transmitted on the air
  369. ;only after the driver goes into transmit state that is
  370. ;after it "pushed" PTT.
  371.  
  372.     clc
  373.     mov dh,NO_ERROR
  374.     ret
  375.  
  376. PacketTooBig:
  377.     call TxFlush8bit                ;???
  378.     call CancelTxBlock              ;cancel the block we were writing
  379.                     ;into Tx buffer
  380.     mov bx,offset errors_out        ;increment errors_out counter
  381.     call inc_dword_bx
  382.  
  383.     stc
  384.     mov dh,CANT_SEND
  385.     ret
  386.  
  387. f_terminate:
  388.     call deinstall_driver
  389.     clc
  390.     mov dh,NO_ERROR
  391.     ret
  392.  
  393. f_get_address:          
  394.     xor cx,cx       ;return zero-length address
  395.     clc
  396.     mov dh,NO_ERROR
  397.     ret
  398.  
  399. f_reset_interface:      jmp short f_not_implemented
  400. f_get_parameters:       jmp short f_not_implemented
  401. f_as_send_pkt:          jmp short f_not_implemented
  402. f_drop_pkt:             jmp short f_not_implemented
  403. f_set_rcv_mode:         jmp short f_not_implemented
  404. f_get_rcv_mode:         jmp short f_not_implemented
  405. f_set_multicast_list:   jmp short f_not_implemented
  406. f_get_multicast_list:   jmp short f_not_implemented
  407.  
  408. f_get_statistics:
  409.     mov _DS[bp],ds
  410.     mov _SI[bp],offset statistics_list
  411.     clc
  412.     mov dh,NO_ERROR
  413.     ret
  414.  
  415. f_set_address:          jmp short f_not_implemented
  416.  
  417. f_not_implemented:              ;non-implemented functions jump here
  418.     stc                     ;set carry to indicate an error
  419.     mov dh,BAD_COMMAND      ;error code = BAD_COMMAND
  420.     ret
  421.  
  422. ;----------------------------------------------------------------------------
  423. ;control 'fullduplex' on/off state while this driver is running
  424. ; CX==1 -> fullduplex   CX==0 -> CSMA
  425. ;dl6zba * 940825, 940827
  426.  
  427. f_fullduplex:
  428.         push ax
  429.         mov  ax,cx
  430.         cmp  ax,0
  431.         je   s_csma
  432.         mov  ah,carrier_sense   ;get current value
  433.         mov  old_cs,ah          ;and save it
  434.         mov  carrier_sense,0    ;set fullduplex mode operation
  435.         jmp  short f_done
  436. s_csma: mov  ah,old_cs          ;get previous value
  437.         mov  carrier_sense,ah   ;set CSMA mode operation
  438. f_done: pop  ax
  439.         clc
  440.         mov  dh,NO_ERROR
  441.         ret
  442. ;----------------------------------------------------------------------------
  443.  
  444.         even
  445. statistics_list label   dword   ;statistics variables
  446. packets_in      dw      0,0     ;as in packet driver specification
  447. packets_out     dw      0,0
  448. bytes_in        dw      0,0
  449. bytes_out       dw      0,0
  450. errors_in       dw      0,0
  451. errors_out      dw      0,0
  452. packets_dropped dw      0,0
  453.  
  454.                 ;extended statistics (not in use yet)
  455. ;PTT_pushes     dw      0,0
  456. ;HDLC_flags     dw      0,0
  457. ;frame_aborts   dw      0,0
  458. ;short_packets  dw      0,0     ;counts valid but too short packets
  459.  
  460. ;Simple routines to increment double words
  461.  
  462. inc_dword_bx_by_ax:             ;increments dword ds:[bx] by ax
  463.     add word ptr [bx],ax
  464.     inc bx
  465.     inc bx
  466.     adc word ptr [bx],0
  467.     dec bx
  468.     dec bx
  469.     ret
  470.  
  471. inc_dword_bx:           ;increments dword ds:[bx] by 1
  472.     add word ptr [bx],1
  473.     inc bx
  474.     inc bx
  475.     adc word ptr [bx],0
  476.     dec bx
  477.     dec bx
  478.     ret
  479.  
  480. ;==============================================================
  481.  
  482.         even
  483. OldTimerISR     dw 0,0  ;saves Timer interrupt service routine address
  484. TimerISRActive  db 0
  485.  
  486.             ;This routine re-routes Timer interrupt so
  487.             ;Timer_ISR is called at every timer tick
  488. Initialize_Timer:
  489.     pushf           ;save flags
  490.     cli             ;disable interrupts while we play with vectors
  491.             ;is it really needed ?
  492.  
  493.     push ax         ;save ax
  494.                 ;now save current timer int. vector
  495.     push es                 ;save es and bx
  496.     push bx
  497.     mov al,8                ;save Timer interrupt vector
  498.     mov ah,35h              ;35h is "get vector" function
  499.     int 21h                 ;call DOS service
  500.     mov OldTimerISR,bx      ;now vector in es:bx - we have to save it
  501.     mov ax,es
  502.     mov OldTimerISR+2,ax
  503.     pop bx                  ;restore es and bx
  504.     pop es
  505.                 ;set new vector
  506.     push dx
  507.     mov al,8                ;Timer vector is number 8
  508.     mov ah,25h              ;25h is "set vector" function
  509.     mov dx,offset Timer_ISR ;ds:dx := Timer_ISR address
  510.     int 21h
  511.     pop dx
  512.  
  513.     pop ax          ;restore ax
  514.  
  515.     popf            ;restore flags (including interrupt flag)
  516.     ret
  517.  
  518.  
  519. Restore_Timer:          ;This routine restores Timer interrupt vector
  520.             ;so Timer_ISR is not called any more
  521.  
  522.     pushf           ;save flags
  523.     push ax
  524.     cli             ;and disable interrupts
  525.  
  526.                     ;restore interrupt vector
  527.     push ds                         ;save ds
  528.     push dx
  529.     mov al,8                        ;timer int. number
  530.     mov ah,25h                      ;"set vector" function
  531.     lds dx,dword ptr OldTimerISR    ;ds:dx := vector address
  532.     int 21h                         ;call DOS
  533.     pop dx
  534.     pop ds                          ;restore ds
  535.  
  536.     pop ax
  537.     popf            ;restore flags
  538.     ret
  539.  
  540. Timer_ISR:
  541.     sti                     ;shall we enable interrupts already here ?
  542.     pushf                   ;first call the old timer service routine
  543.     call dword ptr cs:[OldTimerISR]
  544.     push ax                 ;save all used registers
  545.     push bx                 ;because we are in an interrupt
  546.     push cx
  547.     push dx
  548.     push ds
  549.     mov ax,cs               ;make ds=cs
  550.     mov ds,ax
  551.     mov al,TimerISRActive   ;check for possible recursive call
  552.     and al,al
  553.     jnz TimerISR_already_Active     ;jump if it is so
  554.     mov al,0FFh             ;mark Timer ISR as active now
  555.     mov TimerISRActive,al
  556.     sti                     ;enable interrupts so Tx and Rx can run
  557.                 ;while we process data collected by Rx
  558.     mov al,RxSoundActive    ;if we activated receive beep before
  559.     and al,al
  560.     jz ReadNextRxPeriod
  561.       in al,61h             ;we terminate it now.
  562.       and al,0FCh
  563.       out 61h,al
  564.       xor al,al
  565.       mov RxSoundActive,al
  566.  
  567. ReadNextRxPeriod:
  568.       call ReadRxPeriod     ;read next Rx period into ax
  569.       jnc TimerISR_end      ;jump if buffer empty
  570.       call ProcessPeriod    ;here we process the period
  571.       jmp short ReadNextRxPeriod  ;loop
  572. TimerISR_end:
  573.     xor al,al
  574.     mov TimerISRActive,al
  575. TimerISR_already_active:
  576.     pop ds                  ;restore registers
  577.     pop dx
  578.     pop cx
  579.     pop bx
  580.     pop ax
  581.     iret
  582.  
  583. MaxFrameLen     equ 2050                ;maximum frame length in bytes
  584.                     ;including CRC
  585. MinFrameLen     equ 17                  ;minimal length for AX.25 frame
  586.                     ;any shorter frame is discarded
  587.  
  588. ;AX.25 frames have minimal length of 17 bytes but you may want to
  589. ;make this limit lower if you are going to run other protocol...
  590.  
  591.         even
  592. RxFrameData     db  MaxFrameLen dup(0)  ;frame buffer
  593. RxFrameLen      dw  0                   ;actual frame length (counts bytes)
  594. RxBitCount      db  0                   ;count single data bits
  595. RxFrameValid    db  0                   ;non-zero if frame is valid
  596. SamplePhase     dw  0                   ;time to next sampling point
  597. SampleLevel     dw  0                   ;low=current data bit, high=prev. bit
  598. RxByteReg       db  0                   ;receiver shift register
  599. RxStuffing      db  0
  600. RxShiftReg      db  0
  601. RxSoundActive   db  0
  602.  
  603. ProcessPeriod:                  ;on input ax=period
  604.     mov cx,SampleLevel      ;bit 0,cl=level, bit 0,ch=previous level
  605.     xor cl,1                ;flip level
  606.     mov dx,SamplePhase      ;dx=time to next sample
  607. MoveSampling:
  608.       cmp ax,dx             ;compare SamplePhase with period
  609.       jc PeriodLower        ;jump if Period lower
  610.       sub ax,dx             ;subtract SamplePhase from period
  611.       xor ch,cl             ;xor level with previous level
  612.       call NewRxBit         ;analyze bit in bit 0,ch
  613.       mov ch,cl             ;previous level = level
  614.       mov dx,cl_bit_len     ;load SamplePhase with bit length
  615.       jmp short MoveSampling      ;loop
  616. PeriodLower:
  617.     sub dx,ax               ;subtract period from SamplePhase
  618.     mov SampleLevel,cx      ;save SampleLevel
  619.  
  620.                 ;now comes rather primitive DPLL
  621.     mov ax,cl_bit_len_2     ;load half bit period
  622.     sub ax,dx               ;subtract SamplePhase
  623.                 ;now: dx=SamplePhase, ax=phase error
  624.     cmp ax,walk_step
  625.     jle check_neg
  626.       add dx,walk_step
  627.       jmp short save_SamplePhase
  628. check_neg:        
  629.     neg ax
  630.     cmp ax,walk_step
  631.     jle save_SamplePhase
  632.       sub dx,walk_step
  633.  
  634. ;        add ax,8                ;offset error to compensate
  635. ;                                ;for "sar" arithmetic error
  636. ;        mov cl,4                ;divide the error by 16
  637. ;        sar ax,cl
  638. ;        add dx,ax               ;add correction to SamplePhase
  639.  
  640. save_SamplePhase:        
  641.     mov SamplePhase,dx      ;save SamplePhase
  642.     ret
  643.  
  644. NewRxBit:               ;bit 0,ch = _inverted_ data bit to append to the frame
  645.             ;ax,cx,dx must not be modified
  646.     push ax
  647.     push cx
  648.     push dx
  649.     mov al,RxShiftReg       ;load shift reg.
  650.     shl al,1                ;append data bit (which is still inverted)
  651.     or al,ch
  652.     mov RxShiftReg,al       ;save shift reg.
  653.     cmp al,81h              ;check for HDLC flag (01111110 pattern)
  654.     jz RxFoundFlag          ;jump if so
  655.     test al,7Fh             ;check for invalid frame (7 1s in a row)
  656.     jz RxFrameInvalid       ;jump if so
  657.  
  658.     mov al,RxFrameValid     ;is frame still valid ?
  659.     and al,al
  660.     jz NewRxBit_ret         ;jump if so
  661.     xor ch,1                ;invert bit to append (it was inverted at entry)
  662.     mov al,RxByteReg        ;load byte reg.
  663.     mov ah,RxBitCount
  664.     and ch,1                ;zero to append ?
  665.     jz AppendZeroBit
  666.     ror ch,1                ;carry=data bit (must be 1 here...)
  667.     rcr al,1                ;append data bit to byte buffer
  668.     inc ah                  ;increase bit counter
  669.     inc RxStuffing          ;increase stuffing flag
  670.     jmp short CheckBitCount
  671. AppendZeroBit:
  672.     xor cl,cl
  673.     mov ch,RxStuffing       ;check for stuffing
  674.     cmp ch,5
  675.     mov RxStuffing,cl       ;clear stuffing flag
  676.     jz NewRxBit_ret         ;avoid adding zero bit
  677.       shr al,1              ;append zero bit
  678.       inc ah
  679. CheckBitCount:
  680.     mov RxByteReg,al        ;save byte reg.
  681.     mov RxBitCount,ah       ;save bit counter
  682.     test ah,07h             ;check for byte boundary
  683.     jnz NewRxBit_ret
  684.       mov bx,RxFrameLen     ;load frame length
  685.       cmp bx,MaxFrameLen    ;check frame size
  686.       jnc RxFrameInvalid    ;jump if frame would exceed max. length
  687.       mov [RxFrameData+bx],al
  688.       inc bx
  689.       mov RxFrameLen,bx     ;save new frame length
  690. NewRxBit_ret:
  691.     pop dx
  692.     pop cx
  693.     pop ax
  694.     ret
  695. RxFrameInvalid:
  696.     xor al,al
  697.     mov RxFrameValid,al
  698.     jmp short NewRxBit_ret
  699. RxFoundFlag:
  700.     mov al,RxFrameValid     ;frame valid ?
  701.     and al,al
  702.     jz PrepareNewFrame      ;jump if not valid
  703.     mov al,RxBitCount       ;check bit count
  704.     inc al
  705.     test al,07h             ;check if multiply of 8
  706.     jnz PrepareNewFrame     ;jump if not
  707.     mov cx,RxFrameLen       ;check frame length
  708.     cmp cx,MinFrameLen
  709.     jc PrepareNewFrame      ;jump if length less then minimum
  710.     
  711.     mov dx,0FFFFh           ;initialize CRC
  712.     mov bx,offset RxFrameData      ;pass frame bytes through CRC except last two
  713.     sub cx,2                ;decrease length by 2
  714. PassNextRxbyte:
  715.       mov ah,[bx]           ;load next byte
  716.       inc bx
  717.       call CRCpass          ;pass it through CRC
  718.     loop PassNextRxByte
  719.     not dx                  ;negate CRC
  720.     cmp dl,[bx]             ;check lower CRC byte
  721.     jnz BadCRCFrame         ;jump if bad
  722.     inc bx
  723.     cmp dh,[bx]             ;check high CRC byte
  724.     jnz BadCRCFrame         ;jump if bad
  725.  
  726.     mov al,sound            ;if sound effects activated
  727.     and al,al
  728.     jz UpCallAppl
  729.       mov RxSoundActive,al  ;make receive beep
  730.       xor al,al
  731.       out 42h,al
  732.       mov al,2
  733.       out 42h,al
  734.       in al,61h
  735.       or al,3
  736.       out 61h,al
  737.  
  738. UpCallAppl:
  739. ;Frame is OK !!! - do the upcall to the application layer.
  740.     call DoUpCall
  741.  
  742. PrepareNewFrame:
  743.     xor ax,ax               ;null frame lemgth
  744.     mov RxFrameLen,ax
  745.     mov RxBitCount,al       ;null bit count
  746.     mov RxStuffing,al       ;initialize bit stuffing
  747.     mov al,0FFh
  748.     mov RxFrameValid,al     ;mark frame as valid
  749.     jmp short NewRxBit_ret
  750.  
  751. BadCRCFrame:                    ;shall we really count bad CRC packets
  752.                 ;as "errors_in" ?
  753.  
  754.     mov bx,offset errors_in ;increment "errors_in"
  755.     call inc_dword_bx
  756.  
  757.     jmp short PrepareNewFrame     ;abort current frame and make ready for new one
  758.  
  759. DoUpCall:               ;input: RxFrameData contains a valid packet (CRC is OK)
  760.             ;       RxFrameLen contains its length
  761.     push ds
  762.     push es
  763.     push si
  764.     push di
  765.  
  766.     mov bx,offset packets_in        ;increment input packet counter
  767.     call inc_dword_bx
  768.  
  769.     mov cx,RxFrameLen       ;load packet length
  770.     sub cx,2                ;exclude CRC
  771.  
  772.     mov ax,cx               ;increment input bytes counter by packet length
  773.     mov bx,offset bytes_in
  774.     call inc_dword_bx_by_ax
  775.  
  776.     mov ax,receive_upcall   ;is there a valid upcall address ?
  777.     or ax,receive_upcall+2
  778.     jz drop_packet          ;jump if there is not
  779.  
  780.     mov ax,0                ;flag=0 - first upcall
  781.     mov bx,0                ;handle = 0
  782.     mov di,0                ;set es:di = NULL
  783.     mov es,di
  784.     call dword ptr [receive_upcall] ;first upcall
  785.     mov ax,es               ;check if application returned
  786.     or ax,di                ;valid buffer pointer
  787.     jz drop_packet          ;jump if not
  788. ;       jz DoUpCall_ret
  789.  
  790.     mov si,di               ;make si=di before we alter di (for second upcall)
  791.     mov bx,offset RxFrameData ;copy the packet to es:di
  792.     mov cx,RxFrameLen       ;packet length (exclude CRC)
  793.     sub cx,2
  794. CopyLoop: mov al,[bx]           ;loop over packet bytes
  795.       inc bx                ;would movsb do the job ?
  796.       mov es:[di],al
  797.       inc di
  798.       loop CopyLoop
  799.     mov cx,RxFrameLen       ;again packet len for second call
  800.     sub cx,2                ;and without CRC
  801.     mov ax,es               ;make ds=es (ds not same as cs now !)
  802.     mov ds,ax
  803.     mov bx,0                ;handle 0
  804.     mov ax,1                ;flag=1 - second upcall
  805.     call dword ptr cs:[receive_upcall]      ;second upcall
  806. ;we have to use cs: addressing in above call because we modified ds
  807.  
  808. DoUpCall_ret:
  809.     pop di
  810.     pop si
  811.     pop es
  812.     pop ds
  813.     ret
  814.  
  815. drop_packet:
  816.     mov bx,offset packets_dropped   ;increment dropped packet counter
  817.     call inc_dword_bx
  818.     jmp short DoUpCall_ret
  819.  
  820. ;Note that packets_dropped counts packets refused by the application
  821. ;on the first upcall. I assume application refuses to take the packet
  822. ;by returning NULL pointer
  823.  
  824. ;==============================================================
  825.         even
  826. save_IER        db 0
  827. save_LCR        db 0
  828. save_MCR        db 0
  829. save_DLL        db 0
  830. save_DLM        db 0
  831. save_irq_en     db 0FFh ;save irq enabled in 8259
  832. save_ISR        dw 0,0  ;saved interrupt vector
  833.  
  834. initialize_COM:
  835.     push ax
  836.     push dx
  837.     pushf                   ;save CPU interrupt flag
  838.     cli                     ;disable interrupts
  839.  
  840.     push es
  841.     push bx
  842.     mov al,com_irq          ;save COM interrupt vector
  843.     add al,8
  844.     mov ah,35h
  845.     int 21h
  846.     mov save_ISR,bx
  847.     mov ax,es
  848.     mov save_ISR+2,ax
  849.     pop bx
  850.     pop es
  851.  
  852.     mov al,com_irq          ;set new vector
  853.     add al,8
  854.     mov ah,25h
  855.     mov dx,offset COM_ISR
  856.     int 21h
  857.  
  858.     mov ah,irq_mask
  859.     not ah
  860.     in al,21h               ;read 8259 mask
  861.     or al,ah                ;extract com irq mask
  862.     mov save_irq_en,al      ;save it
  863.     in al,21h               ;enable com irq in 8259
  864.     and al,ah               ;by clearing the right bit.
  865.     out 21h,al
  866.  
  867.     mov al,com_irq_prio     ;IRQ priority requested ?
  868.     and al,al
  869.     jz save_com_reg         ;jump if not
  870.       mov al,com_irq
  871.       sub al,1
  872.       and al,07h
  873.       or al,0C0h
  874.       out 20h,al            ;out 20h,(C0h + (irq-1)&7)
  875.  
  876. save_com_reg:                   ;save COM registers
  877.     mov dx,com_base         ;dx=com_base
  878.     inc dx                  ;dx=IER
  879.     in al,dx                ;save IER
  880.     mov save_IER,al
  881.     xor al,al               ;disable all COM interrupts
  882.     out dx,al
  883.     add dx,2                ;dx=LCR
  884.     in al,dx                ;save LCR
  885.     mov save_LCR,al
  886.     inc dx                  ;dx=MCR
  887.     in al,dx                ;save MCR
  888.     mov save_MCR,al
  889.     dec dx                  ;dx=LCR
  890.     mov al,81h
  891.     out dx,al               ;enable divisor read/write
  892.     sub dx,3                ;dx=com_base=DLL
  893.     in al,dx                ;read DLL
  894.     mov save_DLL,al         ;save it
  895.     inc dx                  ;dx=DLM
  896.     in al,dx                ;read DLM
  897.     mov save_DLM,al         ;save it
  898.     dec dx                  ;dx=com_base=DLL
  899.     mov ax,bd_slot_time     ;set rate divisor
  900.                 ;to slot_time*bd_bit_len
  901.     out dx,al
  902.     inc dx
  903.     xchg al,ah
  904.     out dx,al
  905.     add dx,2                ;dx=LCR
  906.     mov al,01h              ;set 6 data/1 stop/no parity format
  907.     out dx,al
  908.     inc dx                  ;dx=MCR
  909.     mov al,09h              ;set DTR high, RTS low.
  910.     out dx,al               ;and OUT2 high
  911.     sub dx,3                ;dx=IER
  912.     mov al,0Ah
  913.     out dx,al               ;enable TxEmpty and modem status interrupt
  914.     add dx,4                ;dx=LSR
  915.     in al,dx                ;read LSR
  916.     inc dx                  ;to clear possible line status int.
  917.     in al,dx                ;read MSR to clear possible modem status int
  918.     sub dx,6                ;dx=com_base again
  919.     in al,dx                ;read Rx buffer
  920.     in al,dx                ;to clear any possible pending Rx int.
  921.     mov al,000000b          ;load Tx with 000000 char
  922.     out dx,al
  923.     out dx,al
  924.  
  925.     popf                    ;restore interrupt flag
  926.     pop dx
  927.     pop ax
  928.     ret
  929.  
  930. restore_COM:
  931.     pushf
  932.     push ax
  933.     push dx
  934.     cli
  935.  
  936.     push ds
  937.     mov al,com_irq          ;restore interrupt vector
  938.     add al,8
  939.     mov ah,25h
  940.     lds dx,dword ptr save_ISR
  941.     int 21h
  942.     pop ds
  943.  
  944.     in al,21h               ;restore int. enable in 8259
  945.     or al,irq_mask
  946.     and al,save_irq_en
  947.     out 21h,al
  948.  
  949.     mov al,com_irq_prio     ;was IRQ priority requested ?
  950.     and al,al
  951.     jz restore_com_reg      ;jump if not
  952.       mov al,0C7h
  953.       out 20h,al            ;out 20h,C7h
  954.  
  955. restore_com_reg:
  956.     mov dx,com_base
  957.     in al,dx                ;read Rx to clear a possible int.
  958.     in al,dx
  959.     inc dx                  ;dx=IER
  960.     mov al,save_IER         ;restore IER
  961.     out dx,al
  962.     add dx,2                ;dx=LCR
  963.     mov al,save_LCR         ;restore LCR
  964.     out dx,al
  965.     inc dx                  ;dx=MCR
  966.     mov al,save_MCR         ;restore MCR
  967.     out dx,al
  968.     inc dx                  ;dx=LSR
  969.     in al,dx                ;read LSR to clear possible int.
  970.     inc dx                  ;dx=MSR
  971.     in al,dx                ;read MSR to clear possible int.
  972.     sub dx,3                ;dx=LCR
  973.     in al,dx                ;enable rate divisor access
  974.     or al,80h
  975.     out dx,al
  976.     sub dx,3                ;dx=com_base=DLL
  977.     mov al,save_DLL         ;restore rate divisor
  978.     out dx,al
  979.     inc dx                  ;dx=DLM
  980.     mov al,save_DLM
  981.     out dx,al
  982.     add dx,2                ;dx=LCR
  983.     mov al,save_LCR         ;restore LCR again
  984.     out dx,al
  985.  
  986.     pop dx
  987.     pop ax
  988.     popf
  989.     ret
  990.  
  991.         even
  992. TxCountDown     dw 0    ;Down-counter to measure Tx bits within a byte.
  993. TxState         db 0    ;0 = idle
  994.             ;1 = sending head
  995.             ;2 = sending usefull data
  996.             ;3 = sending tail
  997.  
  998. COM_ISR:
  999.     push ax         ;save most often used registers on stack
  1000.     push bx
  1001.     push cx
  1002.     push dx
  1003.     push ds
  1004.  
  1005.     mov ax,cs
  1006.     mov ds,ax
  1007.     mov dx,com_base
  1008.     add dx,2                ;dx=IIR
  1009.  
  1010.     in al,dx                ;load IIR
  1011.     test al,000000001b      ;COM interrupt pending ?
  1012.     jnz No_COM_Service      ;jump if not
  1013.  
  1014.     dec dx                  ;dx=IER
  1015.     xor al,al
  1016.     out dx,al               ;disable all COM interrupts
  1017.     inc dx
  1018.                 ;now check for possible interrupts sources
  1019.     call serv_modem_state   ;dx=IIR and may not be changed
  1020.     call serv_tx_empty
  1021.  
  1022.     mov al,20h              ;tell the interrupt controler
  1023.     out 20h,al              ;that interrupt service is done.
  1024.  
  1025.     dec dx                  ;dx=IER
  1026.     mov al,0Ah
  1027.     out dx,al               ;enable TxEmpty and Modem Status interrupts
  1028.     inc dx
  1029.  
  1030. End_ISR:
  1031.     pop ds                  ;restore saved registers
  1032.     pop dx
  1033.     pop cx
  1034.     pop bx
  1035.     pop ax
  1036.     iret
  1037.  
  1038. No_COM_service:
  1039.     mov al,20h
  1040.     out 20h,al
  1041.     jmp short End_ISR
  1042.  
  1043.     even
  1044. prev_timer_count dw 0
  1045.  
  1046. serv_modem_state:               ;dx=IIR
  1047.     add dx,4                ;dx=MSR
  1048.     in al,dx                ;read MSR
  1049.     sub dx,4                ;dx=IIR
  1050.     test al,00000001b       ;did CTS change state ?
  1051.                 ;one could check any other input here...
  1052.     jz serv_modem_state_ret ;jump if not
  1053.     xor al,al               ;read system timer count
  1054.     out 43h,al
  1055.     in al,40h
  1056.     xchg al,ah
  1057.     in al,40h
  1058.     xchg al,ah              ;Timer value in ax now
  1059.     mov bx,ax               ;subtract previous count (Timer counts _down_ !)
  1060.     xchg ax,prev_timer_count
  1061.     sub ax,bx               ;so now ax contains the period elapsed
  1062.     shr ax,1                ;shift right as lowest bit is always zero
  1063.     call StoreRxPeriod      ;may not change dx,ax
  1064.     call UpdateDataStat
  1065. serv_modem_state_ret:
  1066.     ret                     ;dx=IIR
  1067.  
  1068. serv_tx_empty:          ;dx=IIR
  1069.     add dx,3        ;dx=LSR
  1070.     in al,dx
  1071.     sub dx,3        ;dx=IIR
  1072.     test al,00100000b ;THRE ?
  1073.     jz serv_tx_empty_ret
  1074.     mov bx,2        ;for faster add/sub dx,2
  1075.     sub dx,bx       ;dx=TxData
  1076.     xor al,al       ;load Tx with 0000000 char
  1077.     out dx,al
  1078.     add dx,bx       ;dx=IIR
  1079.     call [word ptr ServTx]  ;bx=2, dx must _not_ be modified
  1080. serv_tx_empty_ret:
  1081.     ret
  1082.  
  1083.     even
  1084. ServTx  dw ServTxIdle
  1085.  
  1086. ServTxIdle:                     ;here a decision about pushing PTT should be taken
  1087.     call Randomize          ;but first update RandomByte
  1088.     call TxBufferEmpty      ;any data to transmit ?
  1089.     jz ClearStat            ;jump if not
  1090.     call TxPTTDecision      ;ask PTT decision circuit for permision
  1091.     jnc ClearStat           ;jump if no permition
  1092.                 ;decision positive - enter transmit mode
  1093.     add dx,2                ;dx=MCR
  1094.     in al,dx
  1095.     or al,2                 ;set RTS high
  1096.     out dx,al               ;thus activate PTT
  1097.     dec dx                  ;dx=LCR
  1098.     mov bl,bd_bit_len       ;bx=bit length in baud divisor units
  1099.     xor bh,bh
  1100.     call SetTxBaudDiv_bx    ;set baud generator to 1 bit len.
  1101.     dec dx                  ;dx=IIR
  1102.     mov ax,tx_head          ;initialize count down
  1103.     mov TxCountDown,ax      ;with Tx head len
  1104.     mov ServTx,offset ServTxHead    ;Transmitter state is "head"
  1105.     ret
  1106. ClearStat:
  1107.     call ClearDataStat
  1108.     ret
  1109.  
  1110.     even
  1111. Tx8bit     dw 0
  1112. NextTxBit  db 0
  1113.  
  1114. ServTxHead:
  1115.     add dx,bx               ;dx=MCR
  1116.     in al,dx                ;flip DTR
  1117.     xor al,1
  1118.     out dx,al
  1119.     sub dx,bx               ;dx=IIR
  1120.     dec TxCountDown
  1121.     jnz ServTxHead_ret
  1122.       mov ServTx,offset ServTxData  ;enter data phase
  1123.       jmp short ReadNext8bit
  1124. ServTxHead_ret:
  1125.     ret
  1126.  
  1127. ServTxData:
  1128.     add dx,bx               ;dx=MCR
  1129.     in al,dx                ;flip DTR if next bit is 1
  1130.     xor al,NextTxBit
  1131.     out dx,al
  1132.     sub dx,bx               ;dx=IIR
  1133.     mov ax,Tx8bit           ;save bit counter and 8bit buffer
  1134.     dec ah                  ;decrement bit counter
  1135.     jz ReadNext8bit         ;jump if zero
  1136.     ror al,1                ;rotate 8bit buffer
  1137.     mov Tx8Bit,ax           ;save it
  1138.     and al,1                ;extract lowest bit
  1139.     mov NextTxBit,al        ;save it
  1140.     ret
  1141. ReadNext8bit:
  1142.     call ReadTx8bit         ;Read next 8 bits
  1143.     jnc TxStartTail         ;jump if buffer empty
  1144.     mov ah,8                ;bit counter = 8
  1145.     not al                  ;invert data bits
  1146.     mov Tx8Bit,ax           ;save bit counter and 8bit buffer
  1147.     and al,1                ;leave lowest bit only
  1148.     mov NextTxBit,al
  1149.     ret
  1150. TxStartTail:
  1151.     mov ax,tx_tail          ;load count down with tx tail length
  1152.     mov TxCountDown,ax
  1153.     mov ServTx,offset ServTxTail
  1154.     ret
  1155.  
  1156. ServTxTail:                     ;bx=2 at entry
  1157.     add dx,bx               ;dx=MCR
  1158.     in al,dx                ;flip DTR
  1159.     xor al,1
  1160.     out dx,al
  1161.     sub dx,bx               ;dx=IIR
  1162.     dec TxCountDown
  1163.     jnz ServTxTail_ret
  1164.       add dx,bx             ;dx=MCR
  1165.       in al,dx
  1166.       or al,1               ;set DTR high
  1167.       and al,0FDh           ;and RTS low
  1168.       out dx,al
  1169.       dec dx                ;dx=LCR
  1170.       mov bx,bd_slot_time   ;set TxEmpty interrupt rate to slot time
  1171.       call SetTxBaudDiv_bx
  1172.       dec dx                ;dx=IIR
  1173.       mov ServTx,offset ServTxIdle
  1174.       call ClearDataStat    ;clear statistics for DCD
  1175. ServTxTail_ret:
  1176.     ret
  1177.  
  1178. SetTxBaudDiv_bx:        ;input: bx=divisor value, dx=LCR
  1179.     mov al,81h      ;enable divisor access
  1180.     out dx,al
  1181.     sub dx,3        ;dx=DLL
  1182.     mov ax,bx       ;write in new baud rate
  1183.     out dx,al
  1184.     inc dx
  1185.     xchg al,ah
  1186.     out dx,al
  1187.     add dx,2        ;dx=LCR again
  1188.     mov al,01h      ;disable divisor access
  1189.     out dx,al
  1190.     ret             ;output: dx=LCR, ax,bx modified
  1191.  
  1192. RandomWord dw 079BDh            ;random number for p-persistance algorithm
  1193.  
  1194. Randomize:                      ;make new random number
  1195.     mov cx,dx               ;save dx
  1196.     xor al,al               ;read system timer count
  1197.     out 43h,al
  1198.     in al,40h               ;low byte
  1199.     xchg al,ah
  1200.     in al,40h               ;high byte
  1201.     xchg al,ah              ;timer value in ax now
  1202.     add ax,RandomWord       ;add previous random word
  1203.     mov bx,65521            ;multiply by 65521
  1204.     mul bx                  ;dx:ax = ax * bx
  1205.     xor ax,dx
  1206.     mov RandomWord,ax       ;save random word
  1207.     mov dx,cx               ;restore dx
  1208.     ret                     ;ax,bx,cx modified
  1209.  
  1210. TxPTTDecision:                  ;this routine decides whether to push PTT now
  1211.                 ;input: dx=IIR
  1212.     mov al,carrier_sense
  1213.     and al,al               ;Full duplex ?
  1214.     jz PushPTT              ;jump if so
  1215.     call sense_carrier      ;Is somebody else transmiting ?
  1216.     jc DontPushPTT          ;jump if so
  1217.                 ;now comes the p-persistance...
  1218.     mov ax,RandomWord       ;ax=pseudo-random word
  1219.     xor ah,al               ;ah=pseudo-random byte
  1220.     mov al,persistance      ;al=persistance
  1221.     cmp ah,al               ;carry when ah<persistance
  1222.     ret
  1223.  
  1224. PushPTT:
  1225.     stc     ;carry:=1 => push PTT
  1226.     ret
  1227.  
  1228. DontPushPTT:
  1229.     clc     ;carry:=0 => do not push PTT
  1230.     ret
  1231.  
  1232. sense_carrier:          ;input: al=carrier mode, dx=MCR
  1233.             ;output: carry=carrier state
  1234.  
  1235.     cmp al,1                ;sense DCD line ?
  1236.     jz sense_DCD
  1237.     cmp al,2                ;sense data transition
  1238.     jz sense_DataTrans
  1239.     cmp al,3                ;be more clever ?
  1240.     jz sense_data
  1241.     clc                     ;otherwise just say carrier=false
  1242.     ret
  1243.  
  1244. sense_DCD:
  1245.     add dx,4        ;dx=MSR
  1246.     in al,dx        ;read MSR
  1247.     sub dx,4        ;dx=IIR
  1248.     rcl al,1        ;carry=DCD state
  1249.     ret
  1250.  
  1251. sense_DataTrans:
  1252.     xor ax,ax               ;at least one data transition
  1253.     cmp ax,DataTransCount   ;since previous time slot ?
  1254.     ret                     ;carry=1 if so
  1255.  
  1256. sense_data:
  1257.     mov bx,DataTransCount
  1258.     cmp bx,2                ;more than 1 transitions counted ?
  1259.     jc few_trans            ;jump if not
  1260.     mov cx,dx               ;save dx
  1261.     mov ax,PeriodDevSum     ;compute sum/count that is the average
  1262.     mov dx,PeriodDevSum+2
  1263.     div bx                  ;ax=periodDevSum div DataTransCount
  1264.     cmp ax,cl_dcd_thres     ;is the average bigger than dcd_threshold ?
  1265.     mov dx,cx               ;restore dx
  1266.     ret                     ;carry=0 (no carrier) if so
  1267. few_trans:
  1268.     clc             ;say carrier=false if there were only few transitions
  1269.     ret
  1270.  
  1271.         even
  1272. DataTransCount  dw 0            ;count input signal transitions
  1273. PeriodDevSum    dw 0,0          ;sums period deviations from round bit lenghts
  1274. prev_period     dw 0
  1275.  
  1276. ClearDataStat:                  ;clear statistics for DCD
  1277.     xor ax,ax
  1278.     mov DataTransCount,ax
  1279.     mov PeriodDevSum,ax
  1280.     mov PeriodDevSum+2,ax
  1281.     ret
  1282. UpdateDataStat:                 ;input: ax=period
  1283.     inc DataTransCount      ;increment data transition counter
  1284.     mov bl,carrier_sense    ;check carrier mode
  1285.     cmp bl,3                ;execute the rest only if carrier mode is 3
  1286.     jnz UpdateDataStat_ret
  1287.     push dx
  1288.     xor dx,dx
  1289.     xchg ax,prev_period     ;prev_period=period;
  1290.     add ax,prev_period      ;ax=period+prev_period
  1291.     add ax,cl_bit_len_2     ;period+=cl_bit_len/2
  1292.     mov bx,cl_bit_len
  1293.     div bx                  ;dx=(period+cl_bit_len_2) div cl_bit_len
  1294.     shr bx,1                ;bx=cl_bit_len/2
  1295.     sub dx,bx               ;dx-=cl_bit_len/2
  1296.     jnc UpdateDevSum        ;if result negative
  1297.       neg dx                ;then negate it (we need absolute value)
  1298. UpdateDevSum:
  1299.     xor ax,ax               ;add dx to PeriodDevSum
  1300.     add PeriodDevSum,dx     ;PeriodDevSum sums deviation of periods
  1301.     adc PeriodDevSum+2,ax   ;from multiple bit lengths
  1302.                 ;for DCD decision
  1303.     pop dx
  1304. UpdateDataStat_ret:
  1305.     ret             ;ax,bx modified
  1306. ;==============================================================
  1307.  
  1308.     even
  1309. TxBufferLen equ 4095             ;Tx buffer length in bytes - must be 2^n-1
  1310. TxBuffer db TxBufferLen+1 dup(0) ;Tx buffer storage
  1311.                  ;this buffer is filled by send_pkt routine
  1312.                  ;and flushed by TxEmpty service routine
  1313.                  ;when tx is in data phase
  1314.                  ;buffer pointers
  1315. TxReadPtr  dw 0         ;points to next byte to read
  1316. TxWritePtr dw 0         ;points to successor of the last valid byte
  1317. TxBlockPtr dw 0         ;Temporary pointer while writing in data block
  1318.             ;this is to ensure that only complete blocks
  1319.             ;will be transmitted
  1320.  
  1321. WriteTx8bit:            ;input: al=byte to write
  1322.     push bx
  1323.     mov bx,TxBlockPtr       ;load block end pointer
  1324.     mov [TxBuffer+bx],al    ;store byte
  1325.     inc bx                  ;increase pointer
  1326.     and bx,TxBufferLen      ;and flip it around
  1327.     cmp bx,TxReadPtr        ;same as read ptr ?
  1328.     jz WriteTx_ret          ;jump if so
  1329.     mov TxBlockPtr,bx
  1330. WriteTx_ret:
  1331.     pop bx
  1332.     ret             ;on exit: Z set = buffer full
  1333.             ;registers unchanged
  1334.  
  1335. ValidateTxBlock:
  1336.     push bx
  1337.     mov bx,TxBlockPtr       ;make write pointer same as block pointer
  1338.     mov TxWritePtr,bx
  1339.     pop bx
  1340.     ret             ;registers unchanged
  1341.  
  1342. CancelTxBlock:
  1343.     push bx
  1344.     mov bx,TxWritePtr       ;make block pointer same as write pointer
  1345.     mov TxBlockPtr,bx
  1346.     pop bx
  1347.     ret             ;registers unchanged
  1348.  
  1349. ReadTx8bit:
  1350.     mov bx,TxReadPtr        ;load read pointer
  1351.     cmp bx,TxWritePtr       ;same as write pointer ?
  1352.     jz ReadTx_ret           ;jump if so
  1353.       mov al,[TxBuffer+bx]  ;read data into AL
  1354.       inc bx                ;increment the read pointer
  1355.       and bx,TxBufferLen    ;and flip it around
  1356.       mov TxReadPtr,bx      ;save it
  1357.       stc                   ;set carry
  1358. ReadTx_ret:
  1359.     ret     ;if carry is 0 => buffer was empty
  1360.         ;bx is modified but this does not matter really
  1361.  
  1362.     even
  1363. Tx8bitBuffer dw 8000h
  1364.  
  1365. AddTxBit:                       ;input: bh=8bit reg., bl=1s counter
  1366.     inc bl                  ;increment 1s counter
  1367.     jc AddTxBit_1
  1368.       mov bl,0              ;clear counter when 0 bit
  1369. AddTxBit_1:
  1370.     rcr bh,1                ;shift the bit into 8bit reg.
  1371.     jnc AddTxBit_ret
  1372.       mov al,bh             ;write reg. into buffer
  1373.       call WriteTx8bit
  1374.       jz AddTxBit_err
  1375.       mov bh,80h
  1376. AddTxBit_ret:
  1377.     clc                     ;clear carry => no problems
  1378.     ret
  1379. AddTxBit_err:
  1380.     stc                     ;set carry => buffer overflow
  1381.     ret
  1382.  
  1383. TxFlush8bit:
  1384.     push ax
  1385.     mov ax,Tx8bitBuffer
  1386.     mov al,ah
  1387.     and al,al
  1388.     jz TxFlush_ret
  1389.     clc
  1390. TxFlush_l:
  1391.     rcr al,1
  1392.     jnc TxFlush_l
  1393.     call WriteTx8bit
  1394. TxFlush_ret:
  1395.     mov ax,8000h
  1396.     mov Tx8bitBuffer,ax
  1397.     pop ax
  1398.     ret             ;output: Z=1 means tx buffer overflow
  1399.  
  1400. AddTxByteDirect:        ;input: ah=byte to add
  1401.     push ax
  1402.     push bx
  1403.     push cx
  1404.     mov bx,Tx8bitBuffer
  1405.     mov cx,8
  1406. AddNextBit:
  1407.       ror ah,1
  1408.       call AddTxBit
  1409.       jc AddTxByte_ret
  1410.     loop AddNextBit
  1411. AddTxByte_ret:
  1412.     mov Tx8bitBuffer,bx
  1413.     pop cx
  1414.     pop bx
  1415.     pop ax
  1416.     ret             ;output: carry=1 if buffer overflow
  1417.  
  1418. AddTxByteStuffing:              ;input: ah=byte to append _with_ bit stuffing
  1419.     push ax
  1420.     push bx
  1421.     push cx
  1422.     mov bx,Tx8bitBuffer
  1423.     mov cx,8
  1424. AddNextBitS:                    ;loop over bits
  1425.       ror ah,1              ;copy next bit to carry flag
  1426.       call AddTxBit
  1427.       jc AddTxByteS_ret
  1428.       cmp bl,5
  1429.       jc AddTxByteS_l
  1430.         clc                 ;if more than 5 1s in a row
  1431.         call AddTxBit       ;append an extra 0 bit
  1432.         jc AddTxByteS_ret   ;jump if buffer overflow
  1433. AddTxByteS_l:
  1434.     loop AddNextBitS
  1435.     clc
  1436. AddTxByteS_ret:
  1437.     mov Tx8bitBuffer,bx
  1438.     pop cx
  1439.     pop bx
  1440.     pop ax
  1441.     ret                     ;output: carry=1 means buffer overflow
  1442.  
  1443.  
  1444. TxBufferEmpty:
  1445.     mov bx,TxReadPtr
  1446.     cmp bx,TxWritePtr
  1447.     ret     ;Z=1 means buffer is empty
  1448.         ;bx is modified
  1449.  
  1450.     even                            ;align buffer to word boudary
  1451. RxBufferLen equ 1023                    ;in words, must be 2^n-1
  1452. RxBuffer   dw RxBufferLen+1 dup(0)      ;Rx buffer storing periods between CTS transition
  1453.                     ;this buffer is filled by CTS transition
  1454.                     ;interrupt routine and flushed
  1455.                     ;by system timer service routine
  1456. RxReadPtr  dw 0                         ;read pointer
  1457. RxWritePtr dw 0                         ;write pointer
  1458.  
  1459. StoreRxPeriod:                  ;must not modify dx,cx
  1460.     mov bx,RxWritePtr       ;load store pointer
  1461.     mov [RxBuffer+bx],ax    ;store the period
  1462.     add bx,2                ;increment the pointer
  1463.     and bx,2*RxBufferLen    ;turn it around if needed
  1464.     mov RxWritePtr,bx       ;save it
  1465.     cmp bx,RxReadPtr        ;same as ReadPtr ?
  1466.     jnz RxStore_ret         ;jump if not
  1467.       mov bx,RxReadPtr
  1468.       add bx,2              ;increment the read pointer
  1469.       and bx,2*RxBufferLen  ;and turn it around
  1470.       mov RxReadPtr,bx
  1471. RxStore_ret:
  1472.     ret
  1473. ;the above routine discards the oldest period when the buffer overflows
  1474.  
  1475. ReadRxPeriod:                   ;modifies only ax
  1476.     push bx
  1477.     mov bx,RxReadPtr        ;load read pointer
  1478.     cmp bx,RxWritePtr       ;same as write pointer ?
  1479.     jz RxRead_ret           ;jump if so
  1480.       mov ax,[RxBuffer+bx]  ;read the period
  1481.       add bx,2              ;increase the pointer
  1482.       and bx,2*RxBufferLen  ;turn it around
  1483.       mov RxReadPtr,bx      ;save it
  1484.       stc                   ;set carry => data is in ax
  1485. RxRead_ret:
  1486.     pop bx
  1487.     ret                     ;if carry is 0 => then buffer was empty
  1488.                 ;otherwise ax = period
  1489.  
  1490. ;==============================================================
  1491. ;CRC computation table and routine
  1492.  
  1493.     even
  1494. CRCtable dw         0,  4489,  8978, 12955, 17956, 22445, 25910, 29887
  1495.      dw     35912, 40385, 44890, 48851, 51820, 56293, 59774, 63735
  1496.      dw      4225,   264, 13203,  8730, 22181, 18220, 30135, 25662
  1497.      dw     40137, 36160, 49115, 44626, 56045, 52068, 63999, 59510
  1498.      dw      8450, 12427,   528,  5017, 26406, 30383, 17460, 21949
  1499.      dw     44362, 48323, 36440, 40913, 60270, 64231, 51324, 55797
  1500.      dw     12675,  8202,  4753,   792, 30631, 26158, 21685, 17724
  1501.      dw     48587, 44098, 40665, 36688, 64495, 60006, 55549, 51572
  1502.      dw     16900, 21389, 24854, 28831,  1056,  5545, 10034, 14011
  1503.      dw     52812, 57285, 60766, 64727, 34920, 39393, 43898, 47859
  1504.      dw     21125, 17164, 29079, 24606,  5281,  1320, 14259,  9786
  1505.      dw     57037, 53060, 64991, 60502, 39145, 35168, 48123, 43634
  1506.      dw     25350, 29327, 16404, 20893,  9506, 13483,  1584,  6073
  1507.      dw     61262, 65223, 52316, 56789, 43370, 47331, 35448, 39921
  1508.      dw     29575, 25102, 20629, 16668, 13731,  9258,  5809,  1848
  1509.      dw     65487, 60998, 56541, 52564, 47595, 43106, 39673, 35696
  1510.      dw     33800, 38273, 42778, 46739, 49708, 54181, 57662, 61623
  1511.      dw      2112,  6601, 11090, 15067, 20068, 24557, 28022, 31999
  1512.      dw     38025, 34048, 47003, 42514, 53933, 49956, 61887, 57398
  1513.      dw      6337,  2376, 15315, 10842, 24293, 20332, 32247, 27774
  1514.      dw     42250, 46211, 34328, 38801, 58158, 62119, 49212, 53685
  1515.      dw     10562, 14539,  2640,  7129, 28518, 32495, 19572, 24061
  1516.      dw     46475, 41986, 38553, 34576, 62383, 57894, 53437, 49460
  1517.      dw     14787, 10314,  6865,  2904, 32743, 28270, 23797, 19836
  1518.      dw     50700, 55173, 58654, 62615, 32808, 37281, 41786, 45747
  1519.      dw     19012, 23501, 26966, 30943,  3168,  7657, 12146, 16123
  1520.      dw     54925, 50948, 62879, 58390, 37033, 33056, 46011, 41522
  1521.      dw     23237, 19276, 31191, 26718,  7393,  3432, 16371, 11898
  1522.      dw     59150, 63111, 50204, 54677, 41258, 45219, 33336, 37809
  1523.      dw     27462, 31439, 18516, 23005, 11618, 15595,  3696,  8185
  1524.      dw     63375, 58886, 54429, 50452, 45483, 40994, 37561, 33584
  1525.      dw     31687, 27214, 22741, 18780, 15843, 11370,  7921,  3960
  1526.  
  1527. CRCpass:                ;input: dx=partial CRC
  1528.     push bx         ;       ah=char to process
  1529.     push ax
  1530.     xor dl,ah
  1531.     mov bl,dl
  1532.     xor bh,bh
  1533.     add bx,bx
  1534.     mov dl,dh
  1535.     xor dh,dh
  1536.     xor dx,[CRCtable+bx]
  1537.     pop ax
  1538.     pop bx
  1539.     ret
  1540.  
  1541. ;CRC in DX must be initialized with 0FFFFh
  1542. ;and inverted after passing through all characters
  1543. ;==============================================================
  1544. ;here is the installation and de-installation code
  1545.  
  1546. deinstall_driver:
  1547.     mov al,packet_int_no    ;disconnect DRVR_ISR
  1548.     mov ah,25h
  1549.     push ds
  1550.     lds dx,dword ptr old_packet_int
  1551.     int 21h
  1552.     pop ds
  1553.  
  1554.     call restore_COM        ;restore COM port state
  1555.                 ;and interrupt vector(s)
  1556.     call Restore_Timer      ;restore Timer interrupt
  1557.  
  1558.     push cs                 ;free memory
  1559.     pop es
  1560.     mov ah,49h
  1561.     int 21h
  1562.     ret
  1563.  
  1564. end_resident:   ;all code after this point will not stay resident
  1565.         ;after the installation is done.
  1566.  
  1567. Int_is_busy:            ;there is already a packet driver installed
  1568.             ;at specified software interrupt
  1569.     call Print_following_string
  1570.     db 'There is already a packet driver at interrupt 0x',0
  1571.     mov dl,packet_int_no
  1572.     call Print_DL_hex
  1573. DoNotInstall:
  1574.     call Print_following_string
  1575.     db 13,10,BOLD,'AX.25 driver has _not_ been installed',NORM,13,10,0
  1576.     mov ax,4C00h                    ;terminate but don't stay resident
  1577.     int 21h
  1578.     ret
  1579.  
  1580. BadUsage:
  1581.     call Print_following_string
  1582.     db 13,10,0
  1583.     mov bx,offset Msg_usage
  1584.     call Print_BX_string
  1585.     jmp short DoNotInstall
  1586.  
  1587. install_driver:
  1588.  
  1589.     call Print_following_string
  1590.     db NORM
  1591.     db 'AX.25 packet driver for RS232 port by Pawel Jalocha',13,10
  1592.     db 'Version of 15th January 1993',13,10
  1593.         db '* test-version featuring fullduplex control - dl6zba 940827 *',13,10
  1594.     db 'Free licence is granted for radio _amateurs_ only',13,10,0
  1595.  
  1596.     call ReadOptions
  1597.     jnc DoPrintParam
  1598.     jmp BadUsage
  1599. DoPrintParam:
  1600.     call PrintParameters
  1601.     call CheckParameters
  1602.     jnc DoComputeSec
  1603.     jmp DoNotInstall
  1604. DoComputeSec:
  1605.     call ComputeSecondaryPar
  1606.  
  1607.     push es
  1608.  
  1609.     mov al,packet_int_no    ;save int vector
  1610.     mov ah,35h
  1611.     int 21h
  1612.     mov old_packet_int,bx
  1613.     mov ax,es
  1614.     mov old_packet_int+2,ax
  1615.  
  1616.     add bx,3                ;check if there is already a packet driver
  1617.     mov di,bx
  1618.     mov si,offset DRVR_ISR +3
  1619.     mov cx,9
  1620. cmp_char: mov al,[si]
  1621.       inc si
  1622.       cmp al,es:[di]
  1623.       jne Int_is_free
  1624.       inc di
  1625.     loop cmp_char
  1626.     jmp Int_is_busy
  1627. Int_is_free:
  1628.  
  1629.     mov al,packet_int_no    ;put a new one in place
  1630.     mov ah,25h
  1631.     mov dx,offset DRVR_ISR
  1632.     int 21h                 ;ds must be equal to cs here
  1633.  
  1634.     mov ax,phd_environ      ;release our environment
  1635.     and ax,ax               ;if any
  1636.     jz skip_envir_release
  1637.     mov es,ax
  1638.     mov ah,49h
  1639.     int 21h
  1640. skip_envir_release:
  1641.     pop es
  1642.  
  1643. ;initialize COM port and interrupt vector(s)
  1644.     call initialize_COM
  1645. ;initialize Timer routine
  1646.     call Initialize_Timer
  1647.  
  1648.     call Print_following_string
  1649.     db BOLD,'AX.25 driver is now installed and initialized',NORM,13,10,0
  1650.  
  1651. ;make the code resident
  1652.     mov ax,3100h
  1653.     mov dx,offset end_resident + 0Fh
  1654.     mov cl,4
  1655.     shr dx,cl
  1656.     int 21h
  1657.     ret
  1658.  
  1659. Msg_usage:
  1660.     db 'ax25 options: (default values in [])',13,10
  1661.     db BOLD,'-?',NORM,' prints this help message',13,10
  1662.     db BOLD,'-i',NORM,'<int_no>(hex) software interrupt number [60]',13,10
  1663.     db BOLD,'-I',NORM,'<irq>(hex)[p][s] COM IRQ number 2..7 [4]',13,10
  1664.     db BOLD,'-B',NORM,'<base>(hex)      COM base address 0..3ff [3f8]',13,10
  1665.     db BOLD,'-b',NORM,'<bit rate>(dec) [1200]',13,10
  1666.     db BOLD,'-c',NORM,'<carrier mode> possible choices are:',13,10
  1667.     db '  -cf = full duplex (do not care about channel busy - just transmit)',13,10
  1668.     db '  -cc = sense DCD modem line',13,10
  1669.     db '  -ct = sense data transitions [default] (hardware DCD)',13,10
  1670.     db '  -cd = deliver carrier signal from data analysis (software DCD)',13,10
  1671.     db BOLD,'-T',NORM,'<threshold>(dec)  software DCD threshold 0..100 [50]',13,10
  1672.     db BOLD,'-s',NORM,'<slot time>(dec) slot time in data bits [120]',13,10
  1673.     db BOLD,'-p',NORM,'<peristance>(dec) persistance/255 [64]',13,10
  1674.     db BOLD,'-h',NORM,'<tx head>(dec)  Transmitter head in data bit units [240]',13,10
  1675.     db BOLD,'-t',NORM,'<tx tail>(dec)  transmitter tail in data bit units [24]',13,10
  1676.     db BOLD,'-S',NORM,' sound effects (beeps for every received frame)',13,10
  1677.     db 0
  1678.  
  1679. ;==============================================================
  1680. ;Here are some routine for printing numbers and strings
  1681.  
  1682. print_following_string: ;prints string following the call - modifies bx !
  1683.             ;string must following "call print_following_string"
  1684.             ;_must_ end with NULL character !
  1685.     pop bx          ;pop return address from the stack
  1686.     push ax         ;so we know where the char. string is
  1687.     push dx
  1688. print_next_char:
  1689.       mov dl,cs:[bx]        ;load next character
  1690.       inc bx
  1691.       and dl,dl             ;NULL char ?
  1692.       jz string_end         ;exit this loop if so
  1693.       mov ah,2              ;otherwise print it
  1694.       int 21h
  1695.     jmp short print_next_char
  1696. string_end:
  1697.     pop dx
  1698.     pop ax
  1699.     push bx                 ;push new return address on stack
  1700.     ret
  1701. print_BX_string:        ;prints string addressed by ds:bx
  1702.     push ax         ;the string must be terminated by NULL char
  1703.     push bx
  1704.     push dx
  1705. print_next_BX_char:             ;loop over characters
  1706.       mov dl,[bx]           ;read next character
  1707.       inc bx
  1708.       and dl,dl             ;NULL char ?
  1709.       jz BX_string_end      ;jump if so
  1710.       mov ah,2              ;otherwise print it
  1711.       int 21h
  1712.     jmp short print_next_BX_char
  1713. BX_string_end:
  1714.     pop dx
  1715.     pop bx
  1716.     pop ax
  1717.     ret
  1718.  
  1719. print_DL_hex:           ;prints in hex byte stored in DL
  1720.     push ax
  1721.     push cx
  1722.     mov cl,4
  1723.     jmp short print_low_byte
  1724. print_DX_hex:           ;prints in hex word stored in DX
  1725.     push ax
  1726.     push cx
  1727.     mov cl,4
  1728.     mov al,dh
  1729.     rol al,cl
  1730.     call print_hex_digit
  1731.     rol al,cl
  1732.     call print_hex_digit
  1733. print_low_byte:
  1734.     mov al,dl
  1735.     rol al,cl
  1736.     call print_hex_digit
  1737.     rol al,cl
  1738.     call print_hex_digit
  1739.     pop cx
  1740.     pop ax
  1741.     ret
  1742.  
  1743. print_hex_digit:        ;prints hex digit stored in AL
  1744.     push ax
  1745.     push dx
  1746.     and al,0Fh
  1747.     cmp al,10
  1748.     jc add_0
  1749.     add al,'a'-'0'-10
  1750. add_0:  add al,'0'
  1751.     mov dl,al
  1752.     mov ah,2
  1753.     int 21h
  1754.     pop dx
  1755.     pop ax
  1756.     ret
  1757.  
  1758. print_DX_dec:           ;print in decimal word stored in DX
  1759.     push ax
  1760.     push bx
  1761.     push cx
  1762.     push dx
  1763.     mov bx,10
  1764.     xor cx,cx
  1765.     mov ax,dx
  1766. calc_next_dig:
  1767.     xor dx,dx
  1768.     div bx
  1769.     push dx
  1770.     inc cx
  1771.     and ax,ax
  1772.     jnz calc_next_dig
  1773. print_next_dig:
  1774.     pop ax
  1775.     call print_dec_digit
  1776.     loop print_next_dig
  1777.     pop dx
  1778.     pop cx
  1779.     pop bx
  1780.     pop ax
  1781.     ret
  1782. print_dec_digit:        ;prints in dec digit stored in AL
  1783.     push ax
  1784.     push dx
  1785.     add al,48
  1786.     mov dl,al
  1787.     mov ah,2
  1788.     int 21h
  1789.     pop dx
  1790.     pop ax
  1791.     ret
  1792.  
  1793. ;==============================================================
  1794. ;Routines to interprete input
  1795.  
  1796. SkipBlanks:             ;mov to first non-SPACE nor TAB char.
  1797.             ;ds:[bx] = string address
  1798. SkipThisChar:
  1799.     mov al,[bx]     ;load next char.
  1800.     inc bx          ;increment pointer
  1801.     cmp al,' '      ;space ?
  1802.     jz SkipThisChar ;jump if SPACE
  1803.     cmp al,9        ;TAB ?
  1804.     jz SkipThisChar ;jump if TAB
  1805.     dec bx          ;if not SPACE nor TAB move pointer back
  1806.     ret             ;ds:bx=address of non-blank character
  1807.             ;al=this character
  1808.  
  1809. ReadOptions:
  1810.     push ax
  1811.     push bx
  1812.     push cx
  1813.     push dx
  1814.     mov bx,81h              ;load offset to command line arguments
  1815. NextOption:
  1816.     call SkipBlanks         ;al=next non-blank char
  1817.     cmp al,13               ;carriage return ?
  1818.     jz ReadOptions_ret
  1819.     cmp al,'-'              ;minus sign ?
  1820.     jz InterpreteOption     ;if so go and interprete following chars
  1821. PrintOptionUsage:               ;otherwise set carry to indicate a problem
  1822. ;       mov bx,offset Msg_usage
  1823. ;       call Print_BX_string
  1824. ReadOptions_err:
  1825.     stc
  1826. ReadOptions_ret:
  1827.     pop dx
  1828.     pop cx
  1829.     pop bx
  1830.     pop ax
  1831.     ret             ;carry=1 => results are _not_ valid
  1832.  
  1833. InterpreteOption:       ;interprete an option
  1834.             ;ds:bx=address of '-' char.
  1835.     inc bx
  1836.     mov al,[bx]     ;load char after '-'
  1837.     inc bx          ;move pointer futher
  1838.     cmp al,'?'      ;question mark ?
  1839.     jz PrintOptionUsage
  1840.     cmp al,'B'      ;B ? (COM base address)
  1841.     jz Opt_base
  1842.     cmp al,'i'
  1843.     jz Opt_interrupt
  1844.     cmp al,'b'
  1845.     jz Opt_baud
  1846.     cmp al,'s'
  1847.     jz Opt_slot
  1848.     cmp al,'S'
  1849.     jz Opt_sound
  1850.     cmp al,'p'
  1851.     jz Opt_persistance
  1852.     cmp al,'h'
  1853.     jz Opt_head
  1854.     cmp al,'t'
  1855.     jz Opt_tail
  1856.     cmp al,'T'
  1857.     jz Opt_DCD_thres
  1858.     cmp al,'I'
  1859.     jz Opt_irq
  1860.     cmp al,'c'
  1861.     jz Opt_carrier
  1862.  
  1863.     call UnknownOption      ;if non of the above says the option
  1864.                 ;was not recognized
  1865.     jmp ReadOptions_err
  1866.  
  1867. Opt_base:                       ;COM base option
  1868.     call ReadHexNumber      ;read hex number following -B
  1869.     mov com_base,dx         ;save it in com_base
  1870.     jmp short NextOption
  1871. Opt_interrupt:
  1872.     call ReadHexNumber
  1873.     mov packet_int_no,dl
  1874.     jmp short NextOption
  1875. Opt_baud:
  1876.     call ReadDecNumber
  1877.     mov bit_rate,dx
  1878.     jmp short NextOption
  1879. Opt_slot:
  1880.     call ReadDecNumber
  1881.     mov slot_time,dl
  1882.     jmp short NextOption
  1883. Opt_sound:
  1884.     mov dl,0FFh
  1885.     mov sound,dl
  1886.     jmp short NextOption
  1887. Opt_persistance:
  1888.     call ReadDecNumber
  1889.     mov persistance,dl
  1890.     jmp short NextOption
  1891. Opt_head:
  1892.     call ReadDecNumber
  1893.     mov tx_head,dx
  1894.     jmp NextOption
  1895. Opt_tail:
  1896.     call ReadDecNumber
  1897.     mov tx_tail,dx
  1898.     jmp NextOption
  1899. Opt_DCD_thres:
  1900.     call ReadDecNumber
  1901.     mov dcd_thres,dx
  1902.     jmp NextOption
  1903.  
  1904.  
  1905. Opt_irq:                        ;COM irq option
  1906.     call ReadHexNumber      ;read hex number following -I
  1907.     mov com_irq,dl          ;save it in com_irq
  1908. irq_sub_opt:                    ;irq sub-options
  1909.     cmp al,'p'              ;is there 'p' after the irq number ?
  1910.     jz irq_prio
  1911.     cmp al,'s'              ;is there 's' after the irq number ?
  1912.     jz irq_share
  1913.     jmp NextOption
  1914.  
  1915. irq_prio:
  1916.     mov al,0FFh
  1917.     mov com_irq_prio,al
  1918.     inc bx
  1919.     mov al,[bx]
  1920.     jmp short irq_sub_opt
  1921.  
  1922. irq_share:
  1923.     mov al,0FFh
  1924.     mov com_irq_share,al
  1925.     inc bx
  1926.     mov al,[bx]
  1927.     jmp short irq_sub_opt
  1928.  
  1929. Opt_carrier:            ;carrier detect method
  1930.     mov al,[bx]
  1931.     cmp al,'f'
  1932.     jz Carr_0
  1933.     cmp al,'c'
  1934.     jz Carr_1
  1935.     cmp al,'t'
  1936.     jz Carr_2
  1937.     cmp al,'d'
  1938.     jz Carr_3
  1939.     call UnknownCarrierOpt
  1940.     jmp ReadOptions_err
  1941. Opt_carr_end:
  1942.     inc bx
  1943.     mov Carrier_sense,al
  1944.     jmp NextOption
  1945.  
  1946. Carr_0: mov al,0
  1947.     jmp short Opt_carr_end
  1948. Carr_1: mov al,1
  1949.     jmp short Opt_carr_end
  1950. Carr_2: mov al,2
  1951.     jmp short Opt_carr_end
  1952. Carr_3: mov al,3
  1953.     jmp short Opt_carr_end
  1954.  
  1955. UnknownCarrierOpt:      ;say the carrier option was not recognized
  1956.     push bx
  1957.     call Print_following_string
  1958.     db BOLD,BLINK,'Unknown option: -c',0
  1959.     mov dl,al
  1960.     mov ah,2
  1961.     int 21h
  1962.     call Print_following_string
  1963.     db NORM,13,10,0
  1964.     pop bx
  1965.     ret
  1966.  
  1967. UnknownOption:          ;say the option not recognized
  1968.     push bx
  1969.     call Print_following_string
  1970.     db BOLD,BLINK,'Unknown option: -',0
  1971.     mov dl,al
  1972.     mov ah,2
  1973.     int 21h
  1974.     call Print_following_string
  1975.     db NORM,13,10,0
  1976.     pop bx
  1977.     ret
  1978.  
  1979. Param_OK db 0           ;"parameters are OK" flag
  1980.  
  1981. CheckParameters:
  1982.     push bx
  1983.     push ax
  1984.     mov al,1
  1985.     mov Param_OK,al
  1986.  
  1987.     call Print_following_string
  1988.     db BOLD,BLINK,0
  1989.  
  1990. ;       mov al,packet_int_no
  1991. ;       cmp al,60h
  1992. ;       jnc int_no_OK
  1993. ;         call Print_following_string
  1994. ;         db 'Packet interrupt number is below 0x60',13,10,0
  1995. ;         xor al,al
  1996. ;         mov Param_OK,al
  1997. ;int_no_OK:
  1998.  
  1999.     mov ax,bit_rate         ;check bit rate
  2000.     cmp ax,300              ;must be >= 300
  2001.     jnc baud_upp
  2002.       call Print_following_string
  2003.       db 'Bauds below 300 bps not supported',13,10,0
  2004.       xor al,al
  2005.       mov Param_OK,al
  2006. baud_upp:
  2007.     cmp ax,14400+1          ;but as well <= 14400
  2008.     jc baud_OK
  2009.       call Print_following_string
  2010.       db 'Bauds above 14400 bps not supported',13,10,0
  2011.       xor al,al
  2012.       mov Param_OK,al
  2013. baud_OK:
  2014.     mov ax,tx_head          ;tx head (TxDelay)
  2015.     cmp ax,8                ;must be >= 8 bits
  2016.     jnc tx_head_OK
  2017.       call Print_following_string
  2018.       db 'Tx head should be at least 8 bits',13,10,0
  2019.       xor al,al
  2020.       mov Param_OK,al
  2021. tx_head_OK:
  2022.     mov ax,tx_tail          ;tx tail
  2023.     cmp ax,8                ;must be >= 8 bits
  2024.     jnc tx_tail_OK
  2025.       call Print_following_string
  2026.       db 'Tx tail should be at least 8 bits',13,10,0
  2027.       xor al,al
  2028.       mov Param_OK,al
  2029. tx_tail_OK:
  2030.     mov al,slot_time        ;slot time
  2031.     cmp al,8                ;must be >= 8 bits
  2032.     jnc slot_time_OK
  2033.       call Print_following_string
  2034.       db 'Slot time should be at least 8 bits',13,10,0
  2035.       xor al,al
  2036.       mov Param_OK,al
  2037. slot_time_OK:
  2038.     mov ax,com_base         ;com base
  2039.     cmp ax,400h             ;must be < 400h
  2040.     jc com_base_OK
  2041.       call Print_following_string
  2042.       db 'COM base address should be in the range 0..3ff',13,10,0
  2043.       xor al,al
  2044.       mov Param_OK,al
  2045. com_base_OK:
  2046.     mov al,com_irq          ;com irq
  2047.     cmp al,2                ;must be > 2
  2048.     jnc com_irq_upp
  2049. wrong_irq:
  2050.       call Print_following_string
  2051.       db 'COM irq should be between 2 and 7',13,10,0
  2052.       xor al,al
  2053.       mov Param_OK,al
  2054.       jmp short com_irq_OK
  2055. com_irq_upp:
  2056.       cmp al,8              ;and < 8 as well
  2057.       jnc wrong_irq
  2058. com_irq_OK:
  2059.     mov ax,dcd_thres
  2060.     cmp ax,101
  2061.     jc dcd_thres_OK
  2062.       call Print_following_string
  2063.       db 'DCD threshold must be between 0 and 100',13,10,0
  2064.       xor al,al
  2065.       mov Param_OK,al
  2066. dcd_thres_OK:
  2067.     
  2068.     mov al,Param_OK
  2069.     shr al,1
  2070.     cmc
  2071.     jnc CheckParam_ret
  2072. ;        call Print_following_string
  2073. ;        db 'ax25 driver can not accept these parameters',7,13,10,0
  2074.     stc
  2075.     
  2076. CheckParam_ret:
  2077.     pushf                           ;save flags to preserve carry
  2078.     call Print_following_string
  2079.     db NORM,0
  2080.     popf
  2081.  
  2082.     pop bx
  2083.     pop ax
  2084.     ret
  2085.  
  2086. ComputeSecondaryPar:            ;computes secondary parameters
  2087.                 ;after the primary ones are defined
  2088.     push ax
  2089.     push bx
  2090.     push cx
  2091.     push dx
  2092.  
  2093.     mov al,1                ;take com_irq
  2094.     mov cl,com_irq
  2095.     shl al,cl               ;and compute bit mask
  2096.     mov irq_mask,al
  2097.  
  2098.                 ;compute bit length for the transmitter
  2099.                 ;in UART baud rate generator ticks
  2100.     mov ax,14400
  2101.     xor dx,dx               ;dx:ax := 14400
  2102.     mov cx,bit_rate         ;cx := bit_rate
  2103.     mov bx,cx
  2104.     shr bx,1                ;bx := bit_rate/2
  2105.     add ax,bx               ;dx:ax := 14400 + bit_rate/2
  2106.     adc dx,0
  2107.     div cx                  ;ax:=(14400 + bit_rate/2) div bit_rate
  2108.     mov bd_bit_len,al       ;save bit length
  2109.  
  2110.     mov ax,14400            ;correct bit rate to a round value
  2111.     xor dx,dx               ;dx:ax := 14400
  2112.     mov cl,bd_bit_len
  2113.     xor ch,ch               ;cx := bd_bit_len
  2114.     div cx                  ;ax := 14400 div bd_bit_len
  2115.     xchg ax,bit_rate        ;bit_rate <-> ax
  2116.     cmp ax,bit_rate         ;rates are indeed different ?
  2117.     jz Compute_cl_bit_len   ;jump if not
  2118.       call Print_following_string   ;notify about speed adjustments
  2119.       db 'Bit rate adjusted to ',0
  2120.       mov dx,bit_rate
  2121.       call Print_DX_dec
  2122.       call Print_following_string
  2123.       db ' bps',13,10,0
  2124.  
  2125. Compute_cl_bit_len:             ;compute bit length for the receiver
  2126.                 ;in timer ticks
  2127.     mov ax,13532
  2128.     mov dx,18               ;dx:ax := 1193180
  2129.     mov cx,bit_rate         ;cx := bit_rate
  2130.     mov bx,cx
  2131.     shr bx,1                ;bx := bit_rate/2
  2132.     add ax,bx               ;dx:ax := 1193180 + bit_rate/2
  2133.     adc dx,0
  2134.     div cx                  ;ax := (1193180 + bit_rate/2) div bit_rate
  2135.     mov cl_bit_len,ax       ;save bit length
  2136.     shr ax,1                ;ax := bit_length /2
  2137.     adc ax,0                ;if carry then ax+=1
  2138.     mov cl_bit_len_2,ax     ;save half bit length
  2139.     
  2140.     mov ax,100
  2141.     sub ax,dcd_thres        ;ax := 100 - dcd_thres
  2142.     mul cl_bit_len          ;ax := ((100 - dcd_thres)*cl_bit_len)
  2143.     mov cx,400
  2144.     div cx                  ;ax := ax div 400
  2145.     mov cl_dcd_thres,ax     ;save threshold for software DCD
  2146.     
  2147.     mov ax,cl_bit_len       ;compute walk_step for DPLL
  2148.     xor dx,dx
  2149.     mov cl,walk_step_div
  2150.     xor ch,ch
  2151.     div cx
  2152.     mov walk_step,ax
  2153.  
  2154.     mov ah,bd_bit_len
  2155.     mov al,slot_time
  2156.     mul ah                  ;ax := bd_bit_len * slot_time
  2157.     mov bd_slot_time,ax
  2158.  
  2159.     pop dx
  2160.     pop cx
  2161.     pop bx
  2162.     pop ax
  2163.     ret
  2164.  
  2165. PrintParameters:                ;prints actual parameters of the driver
  2166.     push ax
  2167.     push bx
  2168.     push cx
  2169.     push dx
  2170.     call print_following_string
  2171.     db 'Actual ax25 driver parameters:',13,10,0
  2172.  
  2173.     call Print_following_string
  2174.     db 'Service interrupt ',BOLD,'0x',0
  2175.     mov dl,packet_int_no
  2176.     call Print_DL_hex
  2177.     call Print_following_string
  2178.     db NORM,13,10,'COM I/O base ',BOLD,'0x',0
  2179.     mov dx,com_base
  2180.     call Print_DX_hex
  2181.  
  2182.     call Print_following_string
  2183.     db NORM,'  IRQ ',BOLD,'0x',0
  2184.     mov dl,com_irq
  2185.     call Print_DL_hex
  2186.  
  2187.     mov al,com_irq_prio
  2188.     and al,al
  2189.     jz skip_irq_prio
  2190.       call Print_following_string
  2191.       db ' [prior]',0
  2192. skip_irq_prio:
  2193.  
  2194.     mov al,com_irq_share
  2195.     and al,al
  2196.     jz skip_irq_shared
  2197.       call Print_following_string
  2198.       db ' [shared]',0
  2199. skip_irq_shared:
  2200.  
  2201.     call Print_following_string
  2202.     db NORM,'  Bit rate ',BOLD,0
  2203.     mov dx,bit_rate
  2204.     mov cx,dx                       ;keep data rate in cx
  2205.     call Print_DX_dec
  2206.     call Print_following_string
  2207.     db ' bps',NORM,13,10,'Tx head ',BOLD,0
  2208.     mov dx,tx_head
  2209.     call Print_DX_dec
  2210.     call Print_following_string
  2211.     db ' bits ',NORM,'(',0
  2212.     mov ax,1000     ;compute tx_head in ms units
  2213.     mul dx
  2214.     div cx          ;ax=tx_head*1000/bit_rate
  2215.     mov dx,ax
  2216.     call print_DX_dec
  2217.     call Print_following_string
  2218.     db 'ms)   Tx tail ',BOLD,0
  2219.     mov dx,tx_tail
  2220.     call Print_DX_dec
  2221.     call Print_following_string
  2222.     db ' bits ',NORM,'(',0
  2223.     mov ax,1000     ;compute tx_tail in ms units
  2224.     mul dx
  2225.     div cx          ;ax=tx_tail*1000/bit_rate
  2226.     mov dx,ax
  2227.     call Print_DX_dec
  2228.     call Print_following_string
  2229.     db 'ms)',13,10,'Slot time ',BOLD,0
  2230.     mov dl,slot_time
  2231.     xor dh,dh
  2232.     call Print_DX_dec
  2233.     call Print_following_string
  2234.     db ' bits ',NORM,'(',0
  2235.     mov ax,1000     ;compute slot_time in ms units
  2236.     mul dx
  2237.     div cx          ;ax=slot_time*1000/bit_rate
  2238.     mov dx,ax
  2239.     call Print_DX_dec
  2240.     call Print_following_string
  2241.     db 'ms)   p-persistance ',BOLD,0
  2242.     mov dl,persistance
  2243.     xor dh,dh
  2244.     call Print_DX_dec
  2245.     call Print_following_string
  2246.     db NORM,'/255',13,10,'Carrier mode: ',BOLD,0
  2247.     mov bl,carrier_sense
  2248.     xor bh,bh
  2249.     add bx,bx
  2250.     mov bx,[msg_carrier_modes+bx]
  2251.     call Print_BX_string
  2252.     call Print_following_string
  2253.     db 13,10,NORM,0
  2254.     
  2255.     mov al,carrier_sense
  2256.     cmp al,3
  2257.     jnz PrintSoundParam
  2258.       call Print_following_string
  2259.       db 'Software DCD threshold: ',BOLD,0
  2260.       mov dx,dcd_thres
  2261.       call Print_DX_dec
  2262.       call Print_following_string
  2263.       db NORM,13,10,0
  2264. PrintSoundParam:
  2265.     mov al,sound
  2266.     and al,al
  2267.     jz PrintParameters_ret
  2268.       call Print_following_string
  2269.       db BOLD,'Sound effects',NORM,' activated',13,10,0
  2270. PrintParameters_ret:
  2271.     call Print_following_string
  2272.     db 13,10,0
  2273.     
  2274.     pop dx
  2275.     pop cx
  2276.     pop bx
  2277.     pop ax
  2278.     ret
  2279.  
  2280.     even
  2281. msg_carrier_modes   label word
  2282.     dw offset Msg_full_duplex
  2283.     dw offset Msg_sense_DCD
  2284.     dw offset Msg_sense_trans
  2285.     dw offset Msg_sense_DPLL
  2286.  
  2287. Msg_full_duplex db 'do not sense carrier => full duplex',0
  2288. Msg_sense_DCD   db 'sense DCD modem line',0
  2289. Msg_sense_trans db 'sense data transitions',0
  2290. Msg_sense_DPLL  db 'deliver carrier from data analysis',0
  2291.  
  2292. ReadDecDigit:           ;ds:bx points to a character
  2293.     mov al,[bx]
  2294.     cmp al,'0'
  2295.     jc ReadDecDigit_ret     ;jump if below '0'
  2296.       cmp al,'9'+1
  2297.       cmc
  2298.       jc ReadDecDigit_Ret   ;jump if above '9'
  2299.         sub al,'0'
  2300. ReadDecDigit_ret:
  2301.     ret             ;carry=1 if not a dec digit => then al=the character
  2302.             ;otherwise al=digit value
  2303.  
  2304. ReadDecNumber:          ;ds:bx points to the first digit
  2305.     push cx
  2306.     mov dx,0        ;dx will contain the number
  2307. ReadNextDecDigit:
  2308.     call ReadDecDigit       ;read next digit - al=digit value
  2309.     jc ReadDecNumber_ret    ;jump if this was not a digit
  2310.       inc bx                ;increment pointer
  2311.       xor ah,ah             ;ax=digit value
  2312.       add dx,dx             ;multiply dx by 2
  2313.       mov cx,dx             ;multiply dx by 5
  2314.       add dx,dx
  2315.       add dx,dx
  2316.       add dx,cx
  2317.       add dx,ax             ;add the digit value just read
  2318.     jmp short ReadNextDecDigit
  2319. ReadDecNumber_ret:
  2320.     pop cx
  2321.     ret             ;dx=the number,
  2322.             ;ds:bx *char where the interpretation stopped
  2323.             ;al=this character, ah possibly modified
  2324.  
  2325. ;the routine below accepts only _lowercase_ letters as hex numbers
  2326. ReadHexDigit:           ;ds:bx = digit pointer
  2327.     mov al,[bx]
  2328.     cmp al,'0'
  2329.     jc ReadHexDigit_ret     ;jump if below '0'
  2330.       cmp al,'9'+1
  2331.       cmc
  2332.       jc ReadHexDigit_lett   ;jump if above '9'
  2333.         sub al,'0'
  2334.         jmp short ReadHexDigit_ret
  2335. ReadHexDigit_lett:
  2336.     cmp al,'a'
  2337.     jc ReadHexDigit_ret       ;jump if below 'a'
  2338.       cmp al,'f'+1
  2339.       cmc
  2340.       jc ReadHexDigit_ret     ;jump if above 'f'
  2341.         sub al,'a'-10
  2342. ReadHexDigit_ret:
  2343.     ret             ;carry=1 => not a hex digit, al=char.
  2344.             ;carry=0 => hex digit indeed, al=value
  2345.  
  2346. ReadHexNumber:
  2347.     push cx
  2348.     mov cl,4
  2349.     mov dx,0
  2350. ReadNextHexDigit:
  2351.     call ReadHexDigit
  2352.     jc ReadHexNumber_ret
  2353.       inc bx
  2354.       shl dx,cl     ;multiply dx by 16
  2355.       or dl,al      ;add the digit just read
  2356.     jmp short ReadNextHexDigit
  2357. ReadHexNumber_ret:
  2358.     pop cx
  2359.     ret             ;dx=the number,
  2360.             ;ds:bx *char where the interpretation stopped
  2361.             ;al=this character
  2362.  
  2363. ;==============================================================
  2364.  
  2365. ax25_code       ends
  2366.  
  2367.     end start
  2368.